<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Script Compatibility</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link href="css/default.css" rel="stylesheet" type="text/css">
</head>
<body>

<h1>脚本兼容性</h1>

<p>由于 AutoHotkey Basic 和 AutoHotkey_L 的一些基本差异 (尤其是 Unicode 和 x64 版本), 为 AutoHotkey Basic 编写的脚本可能无法正常运行在 AutoHotkey_L 上. 此文档描述了当前已知的兼容性问题和解决方法.</p>

<h2 id="Format">Unicode 和 ANSI 的比较</h2>
<p>由内置命令, 函数和运算符所需要或返回的存放在内存中的字符串使用特殊的二进制格式, 参考 <i>原生格式</i>. 这种格式取决于使用的 AutoHotkey 版本: <i>Unicode</i> 或 <i>ANSI</i>. 使用一种特殊格式设计的脚本使用错误的 AutoHotkey 版本运行时常常会遇到问题. 如果您不确定您正使用的是哪个版本, 则运行后面的脚本:</p>
<pre>MsgBox % A_IsUnicode ? "Unicode" : "ANSI"</pre>
<p><b>ANSI:</b> 字符串存储为系统默认 ANSI 代码页编码, 和在 AutoHotkey Basic 一样. 每个字符为一个字节 (8 位).</p>
<p><b>Unicode:</b> 字符串存储为 <a href="http://en.wikipedia.org/wiki/UTF-16">UTF-16</a> 编码, 这里每个字符为两个字节 (16 位).</p>
<p>技术上, 一些 Unicode 字符表示为 <i>两个</i> 16-位代码单元, 一起被称为 "代理项对." 同样地, 一些 <a href="http://msdn.microsoft.com/en-us/library/dd317752.aspx">ANSI 代码页</a> (通常称为 <a href="http://msdn.microsoft.com/en-us/library/dd317794.aspx">双字节字符集</a>) 含有一些双字节字符. 然而, 为了方便和实用它们实际上被视为两个单独的 "字符".</p>

<h3>脚本文件</h3>
<p>注意尽管 AutoHotkey 的每个版本仅支持一种特定的在内存中的字符串格式, 不过在脚本源代码文件中可以使用一些不同的编码. 如果包含有非 ASCII 字符的脚本使用的错误的编码, 那么将无法正确加载它们. Ahk2Exe 和 AutoHotkey_L 的 Unicode 版本默认要求 <a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8</a>, 而 ANSI 版本要求 ANSI. 想了解更多信息, 参见 <a href="http://www.autohotkey.net/~Lexikos/AutoHotkey_L/docs/Scripts.htm#cp">脚本文件代码页</a>.</p>

<h3 id="VarSetCapacity">VarSetCapacity</h3>
<p>VarSetCapacity 设置一个变量占用的空间, <i>单位为字节</i>. 由于一个字符占用空间的大小变化决定于字符串的编码格式，所以可能需要进行一些计算：</p>
<pre>VarSetCapacity(ansi_var,    size_in_chars)
VarSetCapacity(unicode_var, size_in_chars * 2)
VarSetCapacity(native_var,  size_in_chars * (A_IsUnicode ? 2 : 1))
VarSetCapacity(native_var, t_size(size_in_chars))  <em>; 参见 <a href="#NumPutGet">下面</a></em>
</pre>VarSetCapacity 内部将添加一个 <i>字符</i> 的容量以确保变量以空终止符结尾。然而，如果使用 <i>非原生</i> 编码的字符串将存储到变量中时为了保持一致性，<code>size_in_chars</code> 通常应该包含空终止符的大小。<p></p>

<h3 id="DllCall">DllCall</h3>
<p>使用 "Str" 类型时，表示字符串使用当前版本原生的编码格式。由于一些函数可能需要或返回特殊格式的字符串，所以有时还需要使用下列的字符串格式：</p>
<table class="info">
  <tr><th></th><th>字符大小</th><th>C / Win32 类型</th><th>编码</th></tr>
  <tr><td class="Syntax" style="text-align:center">WStr</td><td style="text-align:center">16-位</td><td>wchar_t*, WCHAR*, LPWSTR, LPCWSTR</td><td>UTF-16</td></tr>
  <tr><td class="Syntax" style="text-align:center">AStr</td><td style="text-align:center">8-位</td><td>char*, CHAR*, LPSTR, LPCSTR</td><td>ANSI （系统默认 ANSI 代码页）</td></tr>
  <tr><td class="Syntax" style="text-align:center">Str</td><td style="text-align:center">--</td><td>TCHAR*, LPTSTR, LPCTSTR</td><td>相当于 Unicode 版本中的 <b>WStr</b> 和 ANSI 版本中的 <b>AStr</b>。</td></tr>
</table>
<br>
<p>如果 "Str" 或在当前版本中的等价类型用于参数中，那么字符串或变量的地址被传递给函数，否则创建一个期望格式的字符串临时副本进行传递。函数可以修改临时副本，但  <b>不能</b> 写入到这个字符串空终止符右边的任何位置。一般地， "AStr" 和 "WStr" 不应该用于输出参数中。</p>
<p><b>注意：</b> "AStr" 和 "WStr" 用于参数和函数的返回值同样是有效的。</p>
<p>一般而言，如果脚本通过 DllCall 调用接受字符串参数的函数，那么必须采取以下其中一种方式：</p>
<ol>
  <li>如果函数的 Unicode (W) 和 ANSI (A) 都可用，那么为当前版本调用匹配的那个。在下面的例子中, "DeleteFile" 在内部被称为 "DeleteFileA" 或 "DeleteFileW"。由于 "DeleteFile" 自身实际并不存在，DllCall 自动根据当前版本尝试适合的 "A" 或 "W"。<pre>DllCall("DeleteFile", "Ptr", &amp;filename)
DllCall("DeleteFile", "Str", filename)</pre>
  <p>在这个例子中，<code>&amp;filename</code> 准确按原样传递字符串地址，所以函数必须期望接受和 "Str" 类型相同编码格式的字符串。注意在 AutoHotkey Basic 中 "UInt" 必须使用 "Ptr" 代替，但得到的代码可能不兼容 64 位。<p><b>注意：</b> 如果无法根据名称准确找到指定的函数，在 AutoHotkey_L 中不管指定哪个 DLL 都会在函数名称后添加 "A" 或 "W" 后缀查找。然而，AutoHotkey Basic 则仅为 User32.dll，Kernel32.dll，ComCtl32.dll 或 Gdi32.dll 中的函数添加 "A" 后缀。</p></li>
  <li>如果函数仅接受特殊类型的字符串作为输入，那么脚本可能必须使用相应的字符串类型：</li>
  <pre>DllCall("DeleteFileA", "AStr", filename)
DllCall("DeleteFileW", "WStr", filename)</pre>
  <li>如果函数必须修改字符串（非原生格式），那么脚本必须如 <a href="#VarSetCapacity">上面</a> 描述的那样分配缓存并且把缓存的地址传递给函数。如果参数接受输入，脚本还必须把输入字符串转换为适当的格式；这可以用 <a href="commands/StrPutGet.htm">StrPut</a> 实现。</li>
  <p></p>
</ol>

<h3 id="NumPutGet">NumPut / NumGet</h3>
<p>当使用 NumPut 或 NumGet 操作字符串时，对于给定类型的字符串其偏移和类型都必须正确。可以参考下面的代码：</p>
<pre><em>;  8 位/ANSI   字符串:  size_of_char=1  type_of_char="Char"
; 16 位/UTF-16 字符串:  size_of_char=2  type_of_char="UShort"</em>
<i>n</i>th_char := NumGet(var, (<i>n</i>-1)*<i>size_of_char</i>, <i>type_of_char</i>)
NumPut(<i>n</i>th_char, var, (<i>n</i>-1)*<i>size_of_char</i>, <i>type_of_char</i>)</pre>
<p>如果 <code>var</code> 含有原生格式的字符串，那么根据 <code>A_IsUnicode</code> 的值可以确定变量的值。</p>
<pre><i>n</i>th_char := NumGet(var, t_size(<i>n</i>-1), t_char())
NumPut(<i>n</i>th_char, var, t_size(<i>n</i>-1), t_char())

<em>; 为了方便和清晰定义的函数:</em>
t_char() {
    return A_IsUnicode ? "UShort" : "Char"
}
t_size(char_count=1) {
    return A_IsUnicode ? char_count : char_count*2
}</pre>

<h2 id="ptr">指针大小</h2>
<p>指针在 32 位版本（包括 AutoHotkey Basic）中是 4 个字节大小而在 64 位版本中是 8 个字节。使用结构体或 DllCall 的脚本可能需要为在两种平台上正常运行进行考虑。受影响的特殊地方包括：</p>
<ul>
  <li>含有一个或多个指针的结构体字段的偏移计算。</li>
  <li>含有一个或多个指针的结构体大小计算。</li>
  <li>在 <a href="commands/DllCall.htm">DllCall</a>，<a href="commands/NumPut.htm">NumPut</a> 或 <a href="commands/NumGet.htm">NumGet</a> 中使用的类型名称。</li>
</ul>
<p>对于大小和偏移计算，使用 <a href="Variables.htm#PtrSize">A_PtrSize</a>。对于 DllCall，NumPut 和 NumGet，使用适当的 <a href="commands/DllCall.htm">Ptr</a> 类型。</p>
<p>记住一个字段的偏移常常是在它之前所有字段的总大小。同时注意句柄（包括类似 HWND 和 HBITMAP 的类型）实际上是指针类型。</p>
<pre><em>/*
  typedef struct _PROCESS_INFORMATION {
    HANDLE hProcess;    // Ptr
    HANDLE hThread;
    DWORD  dwProcessId; // UInt (4 字节)
    DWORD  dwThreadId;
  } <a href="http://msdn.microsoft.com/en-us/library/ms684873.aspx">PROCESS_INFORMATION</a>, *LPPROCESS_INFORMATION;
*/</em>
VarSetCapacity(pi, A_PtrSize*2 + 8) <em>; Ptr + Ptr + UInt + UInt</em>
DllCall("<a href="http://msdn.microsoft.com/en-us/library/ms682425.aspx">CreateProcess</a>", <span class="dull">&lt;简短地省略&gt;</span>, "Ptr", &amp;pi, <span class="dull">&lt;省略&gt;</span>)
hProcess    := NumGet(pi, 0)         <em>; 默认为 "Ptr".</em>
hThread     := NumGet(pi, A_PtrSize) <em>; </em>
dwProcessId := NumGet(pi, A_PtrSize*2,     "UInt")
dwProcessId := NumGet(pi, A_PtrSize*2 + 4, "UInt")
</pre>

<h2 id="Other">其他变化</h2>

<h3>默认脚本</h3>
<p>没有指定脚本运行 AutoHotkey_L 时，默认脚本文件名和当前可执行文件名相同但扩展名为 "ahk"。想了解更多细节，参见 <a href="Scripts.htm#cmd">传递命令行参数到脚本 </a>。</p>

<h3 id="Names">变量和函数名</h3>
<p>在变量名中字符 [ ] 和 ? 不再合法。因此，? （用于三元操作符）不再要求其两边为空格。也请参见 <a href="Objects.htm#Syntax">对象语法</a>。</p>

<h3 id="Validation">语法检查</h3>
<p>命令名必须以空格，Tab 或逗号终止。与 AutoHotkey Basic 不同，后面的字符都必须符合这个要求：<code>&lt;&gt;:+-*/!~&amp;|^[]</code>. 因此，像 <code>MsgBox&lt; foo</code> 和 <code>If!foo</code> 这样的语法错误可以在加载时捕捉到，而不会作为 <code>MsgBox,&lt; foo</code> 或 <code>If !foo</code> 那样解释。</p>

<h3 id="Transform">Transform</h3>
<p>一些 <i>Transform</i> 子命令在 <b>Unicode</b> 版本中发生了变化：</p>
<ul>
  <li><span class="Syntax">Unicode</span> 不可用。要赋值 Unicode 文本到剪贴板，使用正常赋值。也请参见：<a href="commands/StrPutGet.htm">StrPut/StrGet</a>。</li>
  <li><span class="Syntax">HTML</span> 支持 <a href="commands/Transform.htm#HTML">附加特性</a>。</li>
</ul>


<h3 id="IfIs">If var is</h3>
<p>此命令忽略系统区域设置，除非使用了 <i>StringCaseSense, Locale</i>。</p>

<h3 id="FileRead">FileRead</h3>
<p>在某些情况下 <a href="commands/FileRead.htm#Binary">FileRead</a> 在代码页之间转换文本，因此可能输出错误的二进制数据。</p>

<h3 id="ControlZ">Control-Z</h3>
<p><a href="commands/LoopReadFile.htm">Loop Read</a> 和 <a href="commands/FileReadLine.htm">FileReadLine</a> 不会把字符 Control-Z (0x1A) 解释为文件末尾标记。因此任何的 Control-Z，即使某一个恰好出现在文件末尾，都原样加载。<a href="commands/FileRead.htm">FileRead</a> 已经采用这样的方式。</p>

<h3 id="SetFormat">SetFormat, Integer[Fast], H</h3>
<p>使用大写字母 H 时，十六进制数字中 A-F 也将为大写。不过在 AutoHotkey Basic 中总是使用小写数字。参见 <a href="commands/SetFormat.htm">SetFormat</a>。</p>

<h3>兼容模式</h3>
<p>如果用于运行脚本的 EXE 文件属性中设置 <a href="http://www.microsoft.com/windowsxp/using/helpandsupport/learnmore/appcompat.mspx#ECD">兼容模式</a> 为 Windows 95、98/ME 或 NT4，脚本可能无法正常运行。这是由于兼容模式会把特定的 Windows 版本报告给应用程序, 但预构建的二进制文件不支持这些版本。例如设置兼容模式为 Windows 95 或 98/ME 将使得 <code>MsgBox %A_OSVersion%</code> 报告 <code>WIN_NT4</code>.</p>

<h3 id="Run">Run / RunWait <span class="ver">[AHK_L 57+]</span></h3>
<p>除了自定义操作支持 (<code>Run *verb file</code>) 外，修订号 57 中还把从 <i>Target</i> 参数中提取操作及其参数的方式做了一些改变。具体地：</p>
<ul>
  <li>如果 <i>Target</i> 以引号开始，那么到下一个引号之间的所有内容被视为操作。</li>
  <li>否则首个以空格结束并且为一个现有文件或以 .exe、.bat、.com、.cmd 或 .hta 结尾的子字符串被视为操作。这样就允许 .ahk、.vbs 或 .lnk 这样的文件类型接受参数，同时也允许像以前的版本那样不需要绝对路径运行“已知”的可执行文件例如 wordpad.exe。</li>
</ul>

<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
</body>
</html>
